package com.young.springdemo.config.log;

import com.young.springdemo.config.annotation.LogOperateType;
import com.young.springdemo.config.red.OperateResult;
import com.young.springdemo.config.red.RedTeacherException;
import com.young.springdemo.jpa.entity.LogInfoEntity;
import com.young.springdemo.jpa.repository.LogInfoRepository;
import com.young.springdemo.utils.domain.Result;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.sql.Timestamp;

/**
 * @program: springdemo
 * @description teacher 操作日志切面类
 * @author: Young
 * @create: 2020-05-31 17:24
 **/
@Aspect
@Component
@Slf4j
public class OperateLogAspect {

    @Pointcut(value = "execution(public * com.young.springdemo.controller..*.*(..))")//controller包下所有
    public void pointCut() {
    }

    @Before("pointCut()")
    public void logStart(JoinPoint joinPoint) {
        log.info("开始记录日志的切面操作");
        //todo 空实现
    }

    @After("pointCut()")
    public void logAfter(JoinPoint joinPoint) {
        //todo 空实现
    }

    @AfterReturning(value = "pointCut()", returning = "result")
    public void logAfterReturin(JoinPoint joinPoint, Result result) {
        //记录日志的业务不能影响到主业务;即使日志记录失败,也不要影响主业务
        writeLog(joinPoint,result);
    }

    @Autowired
    private LogInfoRepository logInfoRepository;

    /**
     * @author Young
     * @description 记录日志的业务不能影响到主业务;即使日志记录失败,也不要影响主业务;有异常就catch掉
     * @date 9:51 2020/6/1
     **/
    private void writeLog(JoinPoint joinPoint,Object object) {
        try {
            MethodSignature signature = (MethodSignature)joinPoint.getSignature();
            OperateLog operateLog = signature.getMethod().getAnnotation(OperateLog.class);
            if (null == operateLog){
                return;
            }
            String userName = getUserInfo(joinPoint,operateLog);
            LogOperateType operate= operateLog.operate();
            String result = "失败";
            if (object.getClass().getName().equals(Result.class.getName())){
                //成功
                log.info("操作成功");
                result = OperateResult.OPERATE_RESULT_SUCCESS.getResult();
            }
            if (object.getClass().getName().equals(RedTeacherException.class.getName())) {
                //红名单人员
                log.error("操作红名单人员....,记录日志");
                result = OperateResult.OPERATE_RESULT_RED.getResult();
            }
            LogInfoEntity logInfoEntity = new LogInfoEntity();
            logInfoEntity.setDescription(operateLog.desc());
            logInfoEntity.setStartTime(new Timestamp(System.currentTimeMillis()));
            logInfoEntity.setOperateType(operate.getDesc());
            logInfoEntity.setResult(result);
            logInfoEntity.setUserName(userName);
            logInfoRepository.save(logInfoEntity);
            log.info("日志记录成功");
        }catch (Exception e){
            log.error("日志记录失败[{}],请自觉检查",e.getMessage());
        }
    }

    /**
     * @author Young
     * @description 根据注解获取用户信息
     * @date 9:37 2020/6/1
     **/
    private String getUserInfo(JoinPoint joinPoint, OperateLog operateLog) throws Exception{
        Object[] args = joinPoint.getArgs();//参数集合
        if (operateLog.userIndex()== -1){
            //直接拿第一个参数
            log.info("获取参数中第一个参数是用户信息:[{}]",args[0].toString());
            return args[0].toString();
        };
        int index = operateLog.userIndex();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Class[] parameterTypes = signature.getParameterTypes();
        index = parameterTypes.length > index ? index : 0;//默认用第一个
        Class parameterType = parameterTypes[index];
        Field userField = parameterType.getDeclaredField(operateLog.userField());
        userField.setAccessible(true);//打开私有性
        String userName = userField.get(args[index]).toString();
        log.info("记录日志获取到的对象用户的信息是[{}]:[{}]",operateLog.userField(),userName);
        return userName;
    }


    @AfterThrowing(value = "pointCut()", throwing = "e")
    public void logAfterThrowing(JoinPoint joinPoint, Exception e) throws Throwable{
        //获取异常信息
        log.error("AfterThrowing 执行了.....");
        writeLog(joinPoint,e);
    }

    @Around("pointCut()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        //直接进行处理
        return joinPoint.proceed();
    }
}
